package ca.ericslandry.client;

import java.util.Date;
import java.util.HashMap;

import ca.ericslandry.client.resource.MyConstants;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;

/**
 * GWTUtils is a set of helper classes to make it easier to work with GWT
 */
public final class GwtUtils {
	public static native String md5(String string) /*-{
													function RotateLeft(lValue, iShiftBits) {
													return (lValue<<iShiftBits) | (lValue>>>(32-iShiftBits));
													}

													function AddUnsigned(lX,lY) {
													var lX4,lY4,lX8,lY8,lResult;
													lX8 = (lX & 0x80000000);
													lY8 = (lY & 0x80000000);
													lX4 = (lX & 0x40000000);
													lY4 = (lY & 0x40000000);
													lResult = (lX & 0x3FFFFFFF)+(lY & 0x3FFFFFFF);
													if (lX4 & lY4) {
													return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
													}
													if (lX4 | lY4) {
													if (lResult & 0x40000000) {
													return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
													} else {
													return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
													}
													} else {
													return (lResult ^ lX8 ^ lY8);
													}
													}

													function F(x,y,z) { return (x & y) | ((~x) & z); }
													function G(x,y,z) { return (x & z) | (y & (~z)); }
													function H(x,y,z) { return (x ^ y ^ z); }
													function I(x,y,z) { return (y ^ (x | (~z))); }

													function FF(a,b,c,d,x,s,ac) {
													a = AddUnsigned(a, AddUnsigned(AddUnsigned(F(b, c, d), x), ac));
													return AddUnsigned(RotateLeft(a, s), b);
													};

													function GG(a,b,c,d,x,s,ac) {
													a = AddUnsigned(a, AddUnsigned(AddUnsigned(G(b, c, d), x), ac));
													return AddUnsigned(RotateLeft(a, s), b);
													};

													function HH(a,b,c,d,x,s,ac) {
													a = AddUnsigned(a, AddUnsigned(AddUnsigned(H(b, c, d), x), ac));
													return AddUnsigned(RotateLeft(a, s), b);
													};

													function II(a,b,c,d,x,s,ac) {
													a = AddUnsigned(a, AddUnsigned(AddUnsigned(I(b, c, d), x), ac));
													return AddUnsigned(RotateLeft(a, s), b);
													};

													function ConvertToWordArray(string) {
													var lWordCount;
													var lMessageLength = string.length;
													var lNumberOfWords_temp1=lMessageLength + 8;
													var lNumberOfWords_temp2=(lNumberOfWords_temp1-(lNumberOfWords_temp1
													% 64))/64;
													var lNumberOfWords = (lNumberOfWords_temp2+1)*16;
													var lWordArray=Array(lNumberOfWords-1);
													var lBytePosition = 0;
													var lByteCount = 0;
													while ( lByteCount < lMessageLength ) {
													lWordCount = (lByteCount-(lByteCount % 4))/4;
													lBytePosition = (lByteCount % 4)*8;
													lWordArray[lWordCount] = (lWordArray[lWordCount] |
													(string.charCodeAt(lByteCount)<<lBytePosition));
													lByteCount++;
													}
													lWordCount = (lByteCount-(lByteCount % 4))/4;
													lBytePosition = (lByteCount % 4)*8;
													lWordArray[lWordCount] = lWordArray[lWordCount] |
													(0x80<<lBytePosition);
													lWordArray[lNumberOfWords-2] = lMessageLength<<3;
													lWordArray[lNumberOfWords-1] = lMessageLength>>>29;
													return lWordArray;
													};

													function WordToHex(lValue) {
													var WordToHexValue="",WordToHexValue_temp="",lByte,lCount;
													for (lCount = 0;lCount<=3;lCount++) {
													lByte = (lValue>>>(lCount*8)) & 255;
													WordToHexValue_temp = "0" + lByte.toString(16);
													WordToHexValue = WordToHexValue +
													WordToHexValue_temp.substr(WordToHexValue_temp.length-2,2);
													}
													return WordToHexValue;
													};

													function Utf8Encode(string) {
													string = string.replace(/\r\n/g,"\n");
													var utftext = "";

													for (var n = 0; n < string.length; n++) {

													var c = string.charCodeAt(n);

													if (c < 128) {
													utftext += String.fromCharCode(c);
													}
													else if((c > 127) && (c < 2048)) {
													utftext += String.fromCharCode((c >> 6) | 192);
													utftext += String.fromCharCode((c & 63) | 128);
													}
													else {
													utftext += String.fromCharCode((c >> 12) | 224);
													utftext += String.fromCharCode(((c >> 6) & 63) | 128);
													utftext += String.fromCharCode((c & 63) | 128);
													}

													}

													return utftext;
													};

													var x=Array();
													var k,AA,BB,CC,DD,a,b,c,d;
													var S11=7, S12=12, S13=17, S14=22;
													var S21=5, S22=9 , S23=14, S24=20;
													var S31=4, S32=11, S33=16, S34=23;
													var S41=6, S42=10, S43=15, S44=21;

													string = Utf8Encode(string);

													x = ConvertToWordArray(string);

													a = 0x67452301; b = 0xEFCDAB89; c = 0x98BADCFE; d = 0x10325476;

													for (k=0;k<x.length;k+=16) {
													AA=a; BB=b; CC=c; DD=d;
													a=FF(a,b,c,d,x[k+0], S11,0xD76AA478);
													d=FF(d,a,b,c,x[k+1], S12,0xE8C7B756);
													c=FF(c,d,a,b,x[k+2], S13,0x242070DB);
													b=FF(b,c,d,a,x[k+3], S14,0xC1BDCEEE);
													a=FF(a,b,c,d,x[k+4], S11,0xF57C0FAF);
													d=FF(d,a,b,c,x[k+5], S12,0x4787C62A);
													c=FF(c,d,a,b,x[k+6], S13,0xA8304613);
													b=FF(b,c,d,a,x[k+7], S14,0xFD469501);
													a=FF(a,b,c,d,x[k+8], S11,0x698098D8);
													d=FF(d,a,b,c,x[k+9], S12,0x8B44F7AF);
													c=FF(c,d,a,b,x[k+10],S13,0xFFFF5BB1);
													b=FF(b,c,d,a,x[k+11],S14,0x895CD7BE);
													a=FF(a,b,c,d,x[k+12],S11,0x6B901122);
													d=FF(d,a,b,c,x[k+13],S12,0xFD987193);
													c=FF(c,d,a,b,x[k+14],S13,0xA679438E);
													b=FF(b,c,d,a,x[k+15],S14,0x49B40821);
													a=GG(a,b,c,d,x[k+1], S21,0xF61E2562);
													d=GG(d,a,b,c,x[k+6], S22,0xC040B340);
													c=GG(c,d,a,b,x[k+11],S23,0x265E5A51);
													b=GG(b,c,d,a,x[k+0], S24,0xE9B6C7AA);
													a=GG(a,b,c,d,x[k+5], S21,0xD62F105D);
													d=GG(d,a,b,c,x[k+10],S22,0x2441453);
													c=GG(c,d,a,b,x[k+15],S23,0xD8A1E681);
													b=GG(b,c,d,a,x[k+4], S24,0xE7D3FBC8);
													a=GG(a,b,c,d,x[k+9], S21,0x21E1CDE6);
													d=GG(d,a,b,c,x[k+14],S22,0xC33707D6);
													c=GG(c,d,a,b,x[k+3], S23,0xF4D50D87);
													b=GG(b,c,d,a,x[k+8], S24,0x455A14ED);
													a=GG(a,b,c,d,x[k+13],S21,0xA9E3E905);
													d=GG(d,a,b,c,x[k+2], S22,0xFCEFA3F8);
													c=GG(c,d,a,b,x[k+7], S23,0x676F02D9);
													b=GG(b,c,d,a,x[k+12],S24,0x8D2A4C8A);
													a=HH(a,b,c,d,x[k+5], S31,0xFFFA3942);
													d=HH(d,a,b,c,x[k+8], S32,0x8771F681);
													c=HH(c,d,a,b,x[k+11],S33,0x6D9D6122);
													b=HH(b,c,d,a,x[k+14],S34,0xFDE5380C);
													a=HH(a,b,c,d,x[k+1], S31,0xA4BEEA44);
													d=HH(d,a,b,c,x[k+4], S32,0x4BDECFA9);
													c=HH(c,d,a,b,x[k+7], S33,0xF6BB4B60);
													b=HH(b,c,d,a,x[k+10],S34,0xBEBFBC70);
													a=HH(a,b,c,d,x[k+13],S31,0x289B7EC6);
													d=HH(d,a,b,c,x[k+0], S32,0xEAA127FA);
													c=HH(c,d,a,b,x[k+3], S33,0xD4EF3085);
													b=HH(b,c,d,a,x[k+6], S34,0x4881D05);
													a=HH(a,b,c,d,x[k+9], S31,0xD9D4D039);
													d=HH(d,a,b,c,x[k+12],S32,0xE6DB99E5);
													c=HH(c,d,a,b,x[k+15],S33,0x1FA27CF8);
													b=HH(b,c,d,a,x[k+2], S34,0xC4AC5665);
													a=II(a,b,c,d,x[k+0], S41,0xF4292244);
													d=II(d,a,b,c,x[k+7], S42,0x432AFF97);
													c=II(c,d,a,b,x[k+14],S43,0xAB9423A7);
													b=II(b,c,d,a,x[k+5], S44,0xFC93A039);
													a=II(a,b,c,d,x[k+12],S41,0x655B59C3);
													d=II(d,a,b,c,x[k+3], S42,0x8F0CCC92);
													c=II(c,d,a,b,x[k+10],S43,0xFFEFF47D);
													b=II(b,c,d,a,x[k+1], S44,0x85845DD1);
													a=II(a,b,c,d,x[k+8], S41,0x6FA87E4F);
													d=II(d,a,b,c,x[k+15],S42,0xFE2CE6E0);
													c=II(c,d,a,b,x[k+6], S43,0xA3014314);
													b=II(b,c,d,a,x[k+13],S44,0x4E0811A1);
													a=II(a,b,c,d,x[k+4], S41,0xF7537E82);
													d=II(d,a,b,c,x[k+11],S42,0xBD3AF235);
													c=II(c,d,a,b,x[k+2], S43,0x2AD7D2BB);
													b=II(b,c,d,a,x[k+9], S44,0xEB86D391);
													a=AddUnsigned(a,AA);
													b=AddUnsigned(b,BB);
													c=AddUnsigned(c,CC);
													d=AddUnsigned(d,DD);
													}

													var temp = WordToHex(a)+WordToHex(b)+WordToHex(c)+WordToHex(d);

													return temp.toLowerCase();
													}-*/;

	// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
	// native js helpers
	// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
	public static native String getParamString() /*-{
													return $wnd.location.search;
													}-*/;

	public static HashMap<String, String> parseParamString(String string) {
		String[] ray = string.substring(1, string.length()).split("&");
		HashMap<String, String> map = new HashMap<String, String>();

		for (int i = 0; i < ray.length; i++) {
			GWT.log("ray[" + i + "]=" + ray[i], null);
			String[] substrRay = ray[i].split("=");
			map.put(substrRay[0], substrRay[1]);
		}

		return map;
	}

	// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
	// DOM and RootPanel helpers
	// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

	/**
	 * This method removes the element identified by s from the browser's DOM,
	 * if the element exists
	 */
	public static void removeElementFromDOM(String elementName) {
		notempty(elementName);
		com.google.gwt.user.client.Element loading = DOM.getElementById(elementName);
		try {
			DOM.removeChild(RootPanel.getBodyElement(), loading);
		} catch (Exception e) {
			// do nothing
		}
	}

	/**
	 * This method tries to add the widget to the element in the DOM. If the
	 * element doesn't exist, then it just adds it to the bottom of the DOM.
	 */
	public static void addWidgetToDOM(String elementName, Widget widget) {
		notempty(elementName, "elementName can not be empty or null");
		notnull(widget, "Widget can not be null");

		try {
			RootPanel.get(elementName).add(widget);
		} catch (Exception e) {
			RootPanel.get().add(widget);
		}
	}

	/**
	 * This method tries to add the widget to the element in the DOM. If the
	 * element doesn't exist, then it just adds it to the bottom of the DOM.
	 */
	public static void replaceWidgetAtDOM(String elementName, Widget widget) {
		notempty(elementName, "elementName can not be empty or null");
		notnull(widget, "Widget can not be null");

		try {
			RootPanel target = RootPanel.get(elementName);
			int count = target.getWidgetCount();
			for (int i = 0; i < count; i++)
				target.remove(i);
			target.add(widget);
		} catch (Exception e) {
			RootPanel.get().add(widget);
		}
	}

	public static void addWidgetToDOM(Widget widget) {
		notnull(widget, "Widget can not be empty");

		RootPanel.get().add(widget);
	}

	// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
	// html builders
	// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
	public static String getLinkHTMLText(String name, String link) throws IllegalArgumentException {
		notempty(name);
		notempty(link);

		return "<a href=\"" + link + "\">" + name + "</a>";
	}

	public static String getLinkHTMLText(String name, String link, String target) throws IllegalArgumentException {
		notempty(name);
		notempty(link);

		return "<a href=" + surroundWithQuotes(link) + " target=" + surroundWithQuotes(target) + ">" + name + "</a>";
	}

	public static String surroundWithQuotes(String msg) {
		return "\"" + msg + "\"";
	}

	// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
	// assertions, validations
	// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

	/** checks to see if the given string is empty or null */
	public static final boolean isnotempty(String s) {
		if (s == null)
			return false;
		if (s.trim().equals(""))
			return false;
		return true;
	}

	/** asserts that the given string is not null or empty */
	private static final void notempty(String s) throws IllegalArgumentException {
		if (s == null)
			throw new IllegalArgumentException("String is null");
		if (s.trim().equals(""))
			throw new IllegalArgumentException("String is empty");
	}

	/** asserts that the given string is not null or empty */
	private static final void notempty(String s, String msg) throws IllegalArgumentException {
		if (isnotempty(s)) {
			return;
		} else {
			// check validity of the error msg
			if (isnotempty(msg)) {
				throw new IllegalArgumentException(msg);
			} else {
				throw new IllegalArgumentException("String is empty or null");
			}
		}
	}

	/** asserts that the given widget is not null */
	private static void notnull(Object object, String msg) throws IllegalArgumentException {
		if (object == null) {
			// check the validity of the error msg
			if (isnotempty(msg)) {
				throw new IllegalArgumentException(msg);
			} else {
				throw new IllegalArgumentException("String is empty or null");
			}
		}
	}

	// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
	// application loading message/splash screen management
	// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

	public static void addLoadingMessage(String image, String msg, String div) {
		String imageUrl = GWT.getModuleBaseURL() + image;
		GwtUtils.addWidgetToDOM(div, new HTML("<center>" + msg + " <img align=\"middle\" src=\"" + imageUrl
				+ "\"></center>"));
	}

	public static void replaceLoadingMessageWith(String div, Widget widget) {
		GwtUtils.replaceWidgetAtDOM(div, widget);
	}

	public static String prettyDate(Date date) {
		long time = date.getTime();
		Date now = new Date();
		long diffSecs = (now.getTime() - time) / 1000;
		if (diffSecs < 60) {
			return MyConstants.INSTANCE.justNow();
		} else if (diffSecs < 120) {
			return MyConstants.INSTANCE.oneMinuteAgo();
		} else if (diffSecs < 3600) {
			long minutes = (long) Math.floor(diffSecs / 60);
			return minutes + MyConstants.INSTANCE.minutesAgo();
		} else if (diffSecs < 7200) {
			return MyConstants.INSTANCE.oneHourAgo();
		} else if (diffSecs < 86400) {
			long hours = (long) Math.floor(diffSecs / 3600);
			return hours + MyConstants.INSTANCE.hoursAgo();
		} else if (diffSecs < 172800) {
			return MyConstants.INSTANCE.yesterday();
		} else if (diffSecs < 604800) {
			long days = (long) Math.floor(diffSecs / 86400);
			return days + MyConstants.INSTANCE.daysAgo();
		} else if (diffSecs < 1209600) {
			return MyConstants.INSTANCE.oneWeekAgo();
		} else if (diffSecs < 31536000) {
			long weeks = (long) Math.floor(diffSecs / 604800);
			return weeks + MyConstants.INSTANCE.weeksAgo();
		} else if (diffSecs < 63072000) {
			return MyConstants.INSTANCE.oneYearAgo();
		}
		long years = (long) Math.floor(diffSecs / 31536000);
		return years + MyConstants.INSTANCE.yearsAgo();
	}

	public static void cursorDefault() {
		/*
		 * TODO: Be less of a CSS hammer. A better way to do this would be to
		 * get all the styles from a parameterized widget and remove the
		 * previous cursor style (if any) and then append the new cursor style
		 * to the remaining styles then set those styles to the specified widget
		 */
		RootPanel.get().setStyleName("cursorDefault");
	}

	public static void cursorPointer() {
		RootPanel.get().setStyleName("cursorPointer");
	}

	public static void cursorWait() {
		RootPanel.get().setStyleName("cursorWait");
	}

	public static void cursorCrosshair() {
		RootPanel.get().setStyleName("cursorCrosshair");
	}
}
